package com.vaadin.tutorial.crm.ui.view.list;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.binder.ValidationException;
import com.vaadin.flow.server.StreamResource;
import com.vaadin.flow.shared.Registration;
import com.vaadin.tutorial.crm.backend.entity.Student;
import com.vaadin.tutorial.crm.backend.util.CommonComponent;
import org.apache.commons.io.IOUtils;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;

/**
 * 表单
 * @Author: fan
 * @DateTime: 2021-01-14 15:09
 **/
//FormLayout根据窗口宽度将表单字段显示为一列或者两列
public class StudentForm extends FormLayout {
    private Student student;

    //通过Binder 来绑定表单字段到数据对象，注意两者属性名称要一致
    Binder<Student> binder = new BeanValidationBinder<>(Student.class);

    //输入框
    TextField stuNum = new TextField("编号");
    TextField stuName = new TextField("姓名");
    ComboBox<Student.StuGender> stuGender = new ComboBox<>("性别");
    DatePicker stuBirth = new DatePicker("出生日期");
    TextField stuPhone = new TextField("联系电话");
    TextField phoneFamily = new TextField("家庭联系电话");
    TextField stuMonitor = new TextField("辅导员");
    TextField stuCollege = new TextField("所属学院");

    //上传组件upload
    MemoryBuffer buffer = new MemoryBuffer();
    Upload upload = new Upload(buffer);

    //按钮
    Button save = new Button("保存");
    Button delete = new Button("删除");
    Button close = new Button("取消");

    //模态框
    public static Dialog dialog;

    public StudentForm() {
        addClassName("student-form");
        //绑定数据到组件
        binder.bindInstanceFields(this);

        stuGender.setItems(Student.StuGender.values());

        upload.addSucceededListener(event -> {
            Component component = createComponent(event.getFileName(), buffer.getInputStream());
            try {
                student.setStuPhoto(IOUtils.toByteArray(buffer.getInputStream()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        //添加组件到布局
        this.add(stuNum,
                stuName,
                stuGender,
                stuBirth,
                stuPhone,
                phoneFamily,
                stuMonitor,
                stuCollege,
                upload,
                createButtonsLayout());
    }

    //HorizontalLayout 使用水平布局使得按钮一个紧挨一个水平排放
    private HorizontalLayout createButtonsLayout() {
        save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
        close.addThemeVariants(ButtonVariant.LUMO_TERTIARY);

        save.addClickShortcut(Key.ENTER);
        close.addClickShortcut(Key.ESCAPE);

        //save、delete、close按钮的事件
        save.addClickListener(event -> validateAndSave());
        delete.addClickListener(event -> openDialog());
        close.addClickListener(event -> fireEvent(new CloseEvent(this)));

        binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid()));
        return new HorizontalLayout(save, delete, close);
    }

    // readBean将值从Student复制到内部模型（最后赋值给表单），这样，如果我们取消编辑，就不会意外覆盖值
    public void setStudent(Student student) {
        this.student = student;
        binder.readBean(student);
    }

    //事件，监听表单中组件值的变化
    // Events
    public static abstract class StudentFormEvent extends ComponentEvent<StudentForm> {
        private Student student;

        protected StudentFormEvent(StudentForm source, Student student) {
            super(source, false);
            this.student = student;
        }

        public Student getStudent() {
            return student;
        }
    }

    public static class SaveEvent extends StudentFormEvent {
        SaveEvent(StudentForm source, Student student) {
            super(source, student);
            CommonComponent.open_notifi("新增成功！");
        }
    }

    public static class DeleteEvent extends StudentFormEvent {
        DeleteEvent(StudentForm source, Student student) {
            super(source, student);
            CommonComponent.open_notifi("删除成功！");
            dialog.close();
        }
    }

    public static class CloseEvent extends StudentFormEvent {
        CloseEvent(StudentForm source) {
            super(source, null);
        }
    }

    public <T extends ComponentEvent<?>> Registration addListener(Class<T> eventType,
                                                                  ComponentEventListener<T> listener) {
        return getEventBus().addListener(eventType, listener);
    }

    private void validateAndSave() {
        try {
            binder.writeBean(student);
            fireEvent(new SaveEvent(this, student));
        } catch (ValidationException e) {
            e.printStackTrace();
        }
    }

    //图片组件
    private Component createComponent(String fileName,
                                      InputStream stream) {
        Image image = new Image();
        try {
            byte[] bytes = IOUtils.toByteArray(stream);
            image.getElement().setAttribute("src", new StreamResource(
                    fileName, () -> new ByteArrayInputStream(bytes)));
            try (ImageInputStream in = ImageIO.createImageInputStream(
                    new ByteArrayInputStream(bytes))) {
                final Iterator<ImageReader> readers = ImageIO
                        .getImageReaders(in);
                if (readers.hasNext()) {
                    ImageReader reader = readers.next();
                    try {
                        reader.setInput(in);
                        image.setWidth(reader.getWidth(0) + "px");
                        image.setHeight(reader.getHeight(0) + "px");
                    } finally {
                        reader.dispose();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return image;
    }

    //删除时弹出的模态框
    public void openDialog(){
        dialog = CommonComponent.createDialog("200px", "130px");
        Div div = CommonComponent.createDiv("确认删除吗？", "margin-left", "30px");
        Button confirm = new Button(new Span("确认"), event -> fireEvent(new StudentForm.DeleteEvent(this, student)));
        confirm.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        confirm.getStyle().set("right", "15px");
        Button cancel = new Button(new Span("取消"), event -> dialog.close());
        cancel.addThemeVariants(ButtonVariant.LUMO_ERROR);
        Div div1 = CommonComponent.createDiv("", "margin-left", "40px");
        div1.add(confirm, cancel);
        dialog.add(
                div,
                div1
        );
        dialog.open();
    }
}